home *** CD-ROM | disk | FTP | other *** search
/ APDL Eductation Resources / APDL Eductation Resources.iso / programs / astronomy / skyview / !SkyView / c / Stars < prev    next >
Encoding:
Text File  |  1993-08-26  |  54.2 KB  |  1,580 lines

  1. /********************************************************/
  2. /*               Stars Module for SkyView               */
  3. /*                                                      */
  4. /*                  (c)1992 N P Hawkes                  */
  5. /*                                                      */
  6. /* Displays the stars in the file 'StarData'. Stars are */
  7. /* identified by Flamsteed & Bayer names, constellation */
  8. /* name, B S catalogue number and proper name if any.   */
  9. /********************************************************/
  10.  
  11. #include "menu.h"
  12. #include "bbc.h"
  13. #include "wimpt.h"
  14. #include "dbox.h"
  15. #include "event.h"
  16. #include "res.h"
  17. #include "resspr.h"
  18. #include "sprite.h"
  19. #include "werr.h"
  20.  
  21. #include "sv_header.h"
  22. #include "stars.h"
  23. #include "radec.h"
  24.  
  25. #include <ctype.h>
  26. #include <stdio.h>
  27. #include <stdlib.h>
  28. #include <string.h>
  29.  
  30. /********************************************************/
  31. /*                     Constants                        */
  32. /********************************************************/
  33.  
  34. #define FILE_NAME "StarData"   /* Name of data file.    */
  35. #define DEF_FILE  "StarDefs"   /* File of Disp. defaults*/
  36. #define DISP_NAME ">Stars  "   /* Entry in Display menu.*/
  37. #define SEL_NAME  " Star   "   /* Entry in Select menu. */
  38.  
  39. #define HORALT (REAL)0.0044    /* Altitude of horizon.  */
  40.  
  41. #define MAX_STARS 600   /* Max no. of stars.            */
  42. #define NUMGRPS     8   /* No. of magnitude groups.     */
  43. #define NUMSPRITES 21   /* No. of different star symbols*/
  44. #define SPRNAM_MAX 13   /* Max len of a spr name, plus 1*/   
  45.  
  46. /* Name of Display dialogue box (for changing sprites): */
  47. #define DISP_BOX "StarDisp"
  48.  
  49. /* Define characteristics of sprites.                   */
  50. #define SPR_X0 -12            /* Bounding box of        */
  51. #define SPR_Y0 -12            /* sprite, OS units,      */
  52. #define SPR_X1  14            /* relative to            */
  53. #define SPR_Y1  14            /* plotting position.     */
  54. #define SPR_COL  5            /* Sprite x offset.       */
  55. #define SPR_ROW  4            /* Sprite y offset.       */
  56. #define SPR_MODE MODE_20      /* Sprite mode.           */
  57.  
  58. /* Field lengths in data file.                          */
  59. #define FLAMST_LEN  4 /* Field len for Flamsteed id.    */
  60. #define BAYER_LEN   9 /* Length of Bayer string.        */
  61. #define CONSTELLEN  3 /* Field len for constell. abbrev.*/
  62. #define MAX_PROPER 30 /* Max len of proper name.        */
  63.  
  64. /* Field lengths in Select dialogue boxes.              */
  65. /* Must be large enough for longest item.               */
  66. #define MAX_FIELD  30
  67.  
  68. /* Number of methods of selecting a star:               */
  69. #define SEL_METHODS 4
  70.  
  71. /* Constants identifying method of selection:           */
  72. enum {
  73.   by_proper = 1,
  74.   by_bayer,
  75.   by_flamst,
  76.   by_catlg
  77. };
  78.  
  79. /* Items for Select sub-menu.                           */
  80. #define STARSEL_SUB_NAME "Select Star:"
  81. #define STARSEL_SUB_ITEMS "\
  82.   By proper name,   \
  83.   By Bayer letter,  \
  84.   By Flamsteed No., \
  85.   By BS Catlg. No."
  86.  
  87. /* Constants for Select dialogue boxes.                 */
  88. enum {
  89.   sel_destroy   = -1, /* Response wiping out the dbox.  */
  90.   sel_ok        =  0, /* OK button.                     */
  91.   sel_cancel,         /* Cancel button.                 */
  92.   sel_star_id,        /* IDs of star on offer.          */
  93.   sel_constell_id,    /* Name of star's constellation.  */
  94.   sel_proper,         /* Proper name of star.           */
  95.   sel_search     = 6, /* 'Search' button.               */
  96.   sel_continue   = 7, /* 'Continue' button.             */
  97.   sel_writeable1 = 9, /* First input field in dbox.     */
  98.   sel_writeable2 =11};/* Second input field in dbox.    */
  99.  
  100. /* Constants for Display dialogue box.                  */
  101. enum {
  102.   disp_destroy = -1,  /* Response wiping out the dbox.  */
  103.   disp_ok      =  0,  /* OK button.                     */
  104.   disp_cancel,        /* Cancel button.                 */
  105.   disp_defshow,       /* 'Show defaults' button.        */
  106.   disp_defset,        /* 'Set defaults' button.         */
  107. /* Sprite, dim button and brighten button for mag. grps.*/
  108.   disp_spr0 =  9, disp_dim0, disp_brt0,
  109.   disp_spr1 = 13, disp_dim1, disp_brt1,
  110.   disp_spr2 = 17, disp_dim2, disp_brt2,
  111.   disp_spr3 = 21, disp_dim3, disp_brt3,
  112.   disp_spr4 = 25, disp_dim4, disp_brt4,
  113.   disp_spr5 = 29, disp_dim5, disp_brt5,
  114.   disp_spr6 = 33, disp_dim6, disp_brt6,
  115.   disp_spr7 = 37, disp_dim7, disp_brt7};
  116.  
  117. /********************************************************/
  118. /*                New types of variable.                */
  119. /********************************************************/
  120. /* Structure describing one star.                       */
  121. typedef struct {
  122.   REAL ra;                /* Right ascension (radians). */
  123.   REAL dec;               /* Declination (radians, N +).*/
  124.   int  horiz_id;          /* id in Horiz window.        */
  125.   int  vert_id;           /* id in Vert window.         */
  126.   int  catlg;             /* Yale Bright Star Catalog #.*/
  127.   REAL mag;               /* Magnitude.                 */
  128.   int  maggrp;            /* Magnitude group.           */
  129.   char *proper;           /* Ptr to proper name or to \0*/
  130.   unsigned char flamst;   /* Flamsteed no, or 0 if none.*/
  131.   unsigned char constell; /* Constellation (Andromeda=1)*/
  132.                           /* or 0 if not recorded.      */
  133.   char bayer[BAYER_LEN+1];/* Bayer ID or null string.   */
  134. } starstr;
  135.  
  136. /********************************************************/
  137. /*                  Global Variables                    */
  138. /********************************************************/
  139. static BOOL display_flag = TRUE;  /* 'Enabled' flag.    */
  140. static BOOL list_valid  = FALSE;  /* Is plot list valid?*/
  141. static int moduleid;              /* Module ID.         */
  142.  
  143. static FILE *fileptr;             /* Data file handle.  */
  144.  
  145. /* Main data store:                                     */
  146. static starstr star_data[MAX_STARS];
  147. static int star_count = 0; /* Total no. of stars.       */
  148.  
  149. static char fmt[256];      /*Fmt string for reading data*/
  150.  
  151. static menu starsel_sub_menu;     /* Select sub menu.   */
  152.  
  153. /* ****** Variables for Selection dialogue box: ******* */
  154. static dbox d_sel;
  155.  
  156. /* Names of Selection dialogue boxes:                   */
  157. static char *selbox_names[SEL_METHODS] = {
  158.   "StarSelP",           /*For selection by Proper Name. */
  159.   "StarSelB",           /*For selection by Bayer Letter.*/
  160.   "StarSelF",           /*For selection by Flamsteed No.*/
  161.   "StarSelC"};          /*For selection by Catalogue No.*/
  162.  
  163. /* Previous value of writeable icons (ie value at the   */
  164. /* most recent click on 'OK'):                          */
  165. static char prev_proper[MAX_FIELD+1]     = "";
  166. static char prev_bayer[MAX_FIELD+1]      = "";
  167. static char prev_constell_b[MAX_FIELD+1] = "";
  168. static int  prev_flamst = 0;
  169. static char prev_constell_f[MAX_FIELD+1] = "";
  170. static int  prev_catlg  = -1;
  171.  
  172. /* Current search criteria (ie value at the most recent */
  173. /* click on 'Search'):                                  */
  174. static char search1[MAX_FIELD+1];   /* Interpretation   */
  175. static char search2[MAX_FIELD+1];   /* depends on       */
  176. static int  search_int;             /* search method.   */
  177.  
  178. /* Current search method:                               */
  179. static int method;
  180.  
  181. /* ******* Set up data for star symbol sprites: ******* */
  182.  
  183. /* Array of sprite IDs, one for each star symbol.       */
  184. static sprite_id spr_id[NUMSPRITES];
  185.  
  186. /* Set up bounds of magnitude groups.  Groups are       */
  187. /* numbered from 0 (dimmest) to NUMGRPS-1 inclusive.    */
  188. /* The value in grpbounds[i] is the inclusive lower     */
  189. /* bound for group i.  The implied value of the non-    */
  190. /* existent element 'grpbounds[NUMGRPS-1]' is -infinity.*/
  191. static REAL grpbounds[NUMGRPS-1] = {
  192.   (REAL) 3.5,
  193.   (REAL) 3.0,
  194.   (REAL) 2.5,
  195.   (REAL) 2.0,
  196.   (REAL) 1.5,
  197.   (REAL) 1.0,
  198.   (REAL) 0.5 };
  199.  
  200. /* Set up table specifying which sprite symbol is to be */
  201. /* used for each magnitude group (ie mag group i uses   */
  202. /* sprite 'which_sprite[i]').                           */
  203. static int which_sprite[NUMGRPS] = {
  204.   1,
  205.   2,
  206.   3,
  207.  10,
  208.  11,
  209.  12,
  210.  13,
  211.  20 };
  212.  
  213.  
  214. /* ******  Set up data for star sprites used in  ****** */
  215. /* ******         Display Options dbox:          ****** */
  216.  
  217. static dbox d_disp;        /* dbox for display options. */
  218. static wimp_w disp_handle; /* Window handle of this dbox*/
  219. /*Table of icon handles, one for each mag group's sprite*/
  220. static wimp_i iconhandles[NUMGRPS];
  221.  
  222. /* Array of sprite names, for use in Display dbox.      */
  223. static char disp_names[NUMSPRITES][SPRNAM_MAX];
  224.  
  225. /* Reserve table specifying which sprite is used for    */
  226. /* which mag group in Display dbox:                     */
  227. static int disp_sprites[NUMGRPS];
  228.  
  229.  
  230. /* ******* Set up data for star identification: ******* */
  231.  
  232. /* Set up a pointer to a null string, for stars with no */
  233. /* proper name.                                         */
  234. static char *no_proper_name = "";
  235.  
  236. /* Set up an array of pointers to constellation names   */
  237. /* (nominative case).                                   */
  238. static char *constell_nom[] = {"",
  239.   "Andromeda",  "Antlia",     "Apus",       "Aquarius",   "Aquila",
  240.   "Ara",        "Aries",      "Auriga",     "Bootes",     "Caelum",
  241.   "Camelopardalis",           "Cancer",     "Canes Venatici",
  242.   "Canis Major","Canis Minor","Capricornus","Carina",     "Cassiopeia",
  243.   "Centaurus",  "Cepheus",    "Cetus",      "Chamaeleon", "Circinus",
  244.   "Columba",    "Coma Berenices",           "Corona Australis",
  245.   "Corona Borealis",          "Corvus",     "Crater",     "Crux",
  246.   "Cygnus",     "Delphinus",  "Dorado",     "Draco",      "Equuleus",
  247.   "Eridanus",   "Fornax",     "Gemini",     "Grus",       "Hercules",
  248.   "Horologium", "Hydra",      "Hydrus",     "Indus",      "Lacerta",
  249.   "Leo",        "Leo Minor",  "Lepus",      "Libra",      "Lupus",
  250.   "Lynx",       "Lyra",       "Mensa",      "Microscopium",
  251.   "Monoceros",  "Musca",      "Norma",      "Octans",     "Ophiuchus",
  252.   "Orion",      "Pavo",       "Pegasus",    "Perseus",    "Phoenix",
  253.   "Pictor",     "Pisces",     "Piscis Austrinus",         "Puppis",
  254.   "Pyxis",      "Reticulum",  "Sagitta",    "Sagittarius","Scorpius",
  255.   "Sculptor",   "Scutum",     "Serpens",    "Sextans",    "Taurus",
  256.   "Telescopium","Triangulum", "Triangulum Australe",      "Tucana",
  257.   "Ursa Major", "Ursa Minor", "Vela",       "Virgo",      "Volans",
  258.   "Vulpecula",  NULL};
  259.  
  260. /* Set up an array of pointers to constellation names   */
  261. /* (genitive case).                                     */
  262. static char *constell_gen[] = {"",
  263.   "Andromedae",  "Antliae",     "Apodis",      "Aquarii",     "Aquilae",
  264.   "Arae",        "Arietis",     "Aurigae",     "Bootis",      "Caeli",
  265.   "Camelopardalis",             "Cancri",      "Canum Venaticorum",
  266.   "Canis Majoris","Canis Minoris","Capricorni","Carinae",     "Cassiopeiae",
  267.   "Centauri",    "Cephei",      "Ceti",        "Chamaeleontis","Circini",
  268.   "Columbae",    "Comae Berenices",            "Coronae Australis",
  269.   "Coronae Borealis",           "Corvi",       "Crateris",    "Crucis",
  270.   "Cygni",       "Delphini",    "Doradus",     "Draconis",    "Equulei",
  271.   "Eridani",     "Fornacis",    "Geminorum",   "Gruis",       "Herculis",
  272.   "Horologii",   "Hydrae",      "Hydri",       "Indi",        "Lacertae",
  273.   "Leonis",      "Leonis Minoris","Leporis",   "Librae",      "Lupi",
  274.   "Lyncis",      "Lyrae",       "Mensae",      "Microscopii",
  275.   "Monocerotis", "Muscae",      "Normae",      "Octantis",    "Ophiuchi",
  276.   "Orionis",     "Pavonis",     "Pegasi",      "Persei",      "Phoenicis",
  277.   "Pictoris",    "Piscium",     "Piscis Austrini",            "Puppis",
  278.   "Pyxidis",     "Reticuli",    "Sagittae",    "Sagittarii",  "Scorpii",
  279.   "Sculptoris",  "Scuti",       "Serpentis",   "Sextantis",   "Tauri",
  280.   "Telescopii",  "Trianguli",   "Trianguli Australis",        "Tucanae",
  281.   "Ursae Majoris","Ursae Minoris","Velorum",   "Virginis",    "Volantis",
  282.   "Vulpeculae",  NULL };
  283.  
  284. /* Set up an array of pointers to constellation names   */
  285. /* (abbreviations).                                     */
  286. static char *constell_abb[] = {"",
  287.   "And", "Ant", "Aps", "Aqr", "Aql", "Ara", "Ari", "Aur", "Boo", "Cae",
  288.   "Cam", "Cnc", "CVn", "CMa", "CMi", "Cap", "Car", "Cas", "Cen", "Cep",
  289.   "Cet", "Cha", "Cir", "Col", "Com", "CrA", "CrB", "Crv", "Crt", "Cru",
  290.   "Cyg", "Del", "Dor", "Dra", "Equ", "Eri", "For", "Gem", "Gru", "Her",
  291.   "Hor", "Hya", "Hyi", "Ind", "Lac", "Leo", "LMi", "Lep", "Lib", "Lup",
  292.   "Lyn", "Lyr", "Men", "Mic", "Mon", "Mus", "Nor", "Oct", "Oph", "Ori",
  293.   "Pav", "Peg", "Per", "Phe", "Pic", "Psc", "PsA", "Pup", "Pyx", "Ret",
  294.   "Sge", "Sgr", "Sco", "Scl", "Sct", "Ser", "Sex", "Tau", "Tel", "Tri",
  295.   "TrA", "Tuc", "UMa", "UMi", "Vel", "Vir", "Vol", "Vul", NULL};
  296.  
  297. /********************************************************/
  298. /*                 Function Prototypes                  */
  299. /********************************************************/
  300. static BOOL read_data(starstr *sptr);
  301. static BOOL make_sprite_id(char *namestr, int i, sprite_id *idptr);
  302. static BOOL make_sprite_name(char *namestr, int i, char *spr_name);
  303. static int  maggrp(REAL mag);
  304. static char *trim(char *str);
  305. static void read_optional(char *string, int count);
  306. static int string_search(char *ptrs[], char *targ);
  307. static BOOL begins_with(char *a, char *b);
  308. static void star_buildfn(void);
  309. static BOOL star_displayfn(BOOL *enabptr);
  310. static void load_dispbox(int table[]);
  311. static os_error *set_sprite(int grpnum, int sprnum);
  312. static BOOL check_and_copy(int table1[], int table2[]);
  313. static void toggle_display(BOOL *enabptr);
  314. static BOOL dispbox_buttons(void);
  315. static BOOL table_todisc(void);
  316. static BOOL table_fromdisc(int spr_table[]);
  317. static void star_selectfn(selectfn_reasoncode reason);
  318. static int  selectionboxes(void);
  319. static void selbox_load(void);
  320. static int  selbox_buttons(void);
  321. static BOOL no_return_key(dbox d, void *event, void *handle);
  322. static void save_searchdata(void);
  323. static BOOL read_searchdata(void);
  324. static void find_next_star(int *id_ptr);
  325. static BOOL begins_with_inlist(char *a, char *b);
  326. static BOOL is_same(char *a, char *b);
  327. static void display_id(int id);
  328. static void id_string(int id, char *str);
  329. static void selection_details(void);
  330. static os_error *star_plotfn(int x, int y, int id);
  331. static void star_infofn(int id);
  332.  
  333. /********************************************************/
  334. /*                Initialisation Function               */
  335. /********************************************************/
  336. BOOL star_initfn(int moduleno, modulestr *stars)
  337. {
  338.   int i;
  339.  
  340. /* Record the module number of this module.             */
  341.   moduleid = moduleno;
  342.  
  343. /* Open the data file.  Quit if error.                  */
  344.   fileptr = res_openfile(FILE_NAME, "r");
  345.   if (fileptr == NULL) return FALSE;
  346.  
  347. /* Build format string for reading star data.           */
  348.   sprintf(fmt, "%%%ic%%*c%%%ic%%*c%%%ic %%d %%d %%d %%d %%c %%d %%d %%d %%f",
  349.           FLAMST_LEN, BAYER_LEN, CONSTELLEN);
  350.  
  351. /* Read in star data, one star at a time.  Increment    */
  352. /* star count while reads are successful; stop on       */
  353. /* first failure.  Stay within array bounds.            */
  354.   while (star_count < MAX_STARS && 
  355.          read_data(star_data + star_count))
  356.     star_count++;
  357.  
  358. /* Close data file.  Fatal error if it won't close.     */
  359.   if (fclose(fileptr) != 0)
  360.     werr(FATAL, "Stars Error: Can't close Data file");
  361.  
  362. /* Quit if no stars read in.                            */
  363.   if (star_count == 0) return FALSE;
  364.  
  365. /* Set up Select sub-menu.                              */
  366.   starsel_sub_menu = menu_new(STARSEL_SUB_NAME, STARSEL_SUB_ITEMS);
  367.   if (starsel_sub_menu == NULL) return FALSE;
  368.  
  369. /* Attempt to read default sprite table (which sprite   */
  370. /* to use for which magnitude group).  Leave the hard-  */
  371. /* wired defaults unchanged if errors occurred.         */
  372.   {
  373.     int temp_table[NUMGRPS];
  374.  
  375.     if (table_fromdisc(temp_table))
  376.       for (i=0; i<NUMGRPS; i++)
  377.         which_sprite[i] = temp_table[i];
  378.   }
  379.  
  380. /* Fill in the array of sprite IDs (for plotting) and   */
  381. /* the array of sprite names (for the Display Options   */
  382. /* dbox).  Quit if error.                               */
  383.   for (i=0; i<NUMSPRITES; i++)
  384.   {
  385.     if (!make_sprite_id("star", i, spr_id+i)) return FALSE;
  386.     if (!make_sprite_name("disp", i, disp_names[i])) return FALSE;
  387.   }
  388.  
  389. /* Fill in the table of icon handles.  iconhandle[i] is */
  390. /* the icon handle (within the Display dbox) for the    */
  391. /* sprite for magnitude group i.                        */
  392.   for (i=0; i<NUMGRPS; i++)
  393.     iconhandles[i] = disp_spr0 + 4 * i;
  394.  
  395. /* Fill in the fields of the modulestr.                 */
  396.   stars->buildfn  = star_buildfn;
  397.   stars->selectfn = star_selectfn;
  398.   stars->dispfn   = star_displayfn;
  399.   stars->infofn   = star_infofn;
  400.   stars->initial  = display_flag;
  401.   stars->display_entry = DISP_NAME;
  402.   stars->display_menu  = NULL;
  403.   stars->select_entry  = SEL_NAME;
  404.   stars->select_menu   = starsel_sub_menu;
  405.  
  406.   return TRUE;
  407. }
  408.  
  409. /*------------------------------------------------------*/
  410. /*   Function to build an address-type sprite id corr-  */
  411. /* esponding to the sprite name returned by the function*/
  412. /* make_sprite_name. Returns TRUE if no errors detected.*/
  413. /*------------------------------------------------------*/
  414. static BOOL make_sprite_id(char *namestr, int i, sprite_id *idptr)
  415. {
  416.   char spr_name[SPRNAM_MAX]; /*For built-up sprite name.*/
  417.   sprite_id nametype_id;
  418.  
  419. /* Construct sprite name.                               */
  420.   if (!make_sprite_name(namestr, i, spr_name)) return FALSE;
  421.  
  422. /* Form a name-type sprite id using this name.          */
  423.   nametype_id.s.name = spr_name;
  424.   nametype_id.tag    = sprite_id_name;
  425.  
  426. /* Get address of sprite.  Quit if error.               */
  427.   if (sprite_select_rp(resspr_area(),
  428.                        &nametype_id,
  429.                        &idptr->s.addr) != NULL) return FALSE;
  430.  
  431. /* Set tag to identify *idptr as an address-type id.    */
  432.   idptr->tag = sprite_id_addr;
  433.  
  434.   return TRUE;
  435. }
  436.  
  437. /*------------------------------------------------------*/
  438. /* Function to make a sprite name from the given string */
  439. /* namestr and the given integer i.  The name made is of*/
  440. /* the form ssssnnn, where ssss is the first 9 chars of */
  441. /* the given string (fewer if the string is shorter) and*/
  442. /* nnn is the integer i expressed as 3 digits (000-999).*/
  443. /* String pointed to by spr_name is assumed long enough.*/
  444. /*          Return FALSE if error detected.             */
  445. /*------------------------------------------------------*/
  446. static BOOL make_sprite_name(char *namestr, int i, char *spr_name)
  447. {
  448.   int len;
  449.  
  450.   if (i<0 || i>999) return FALSE;
  451.  
  452. /* Copy at most 9 chars from namestr to spr_name.       */
  453.   strncpy(spr_name, namestr, 9);
  454. /* Add number.                                          */
  455.   len = strlen(namestr);
  456.   if (len > 9) len = 9;
  457.   sprintf(spr_name+len, "%03i", i);
  458.  
  459.   return TRUE;
  460. }
  461.  
  462. /*------------------------------------------------------*/
  463. /* Function to read in star data.  Return TRUE if all   */
  464. /*      non-optional data are read successfully.        */
  465. /*------------------------------------------------------*/
  466. static BOOL read_data(starstr *sptr)
  467. {
  468. /* Items to be read are:                                */
  469. /* Flamsteed number (may be all blanks);                */
  470. /* Bayer id, eg "alpha" or "beta 2" (may be blanks);    */
  471. /* Constellation id (3-char abbrev. May be blanks);     */
  472. /* Catalogue number;                                    */
  473. /* Right ascension in hours, minutes and seconds;       */
  474. /* "N" or "S" to specify north or south declination;    */
  475. /* Declination in degrees, minutes and seconds;         */
  476. /* Magnitude;                                           */
  477. /* Proper name (optional).                              */
  478.  
  479. /* Suitable data may be found in the Astronomical       */
  480. /* Almanac (pp H1 - H31 in 1990 edition) together with  */
  481. /* Norton's Star Atlas.  (See Bibliog. in User Guide.)  */
  482.  
  483. /* Set up buffers for strings which need processing.    */
  484. /* Init to 0 ensures null terminator where needed.      */
  485.   static char flamst_buf[FLAMST_LEN + 1];
  486.   static char constellbuf[CONSTELLEN + 1];
  487.   static char name_buf[MAX_PROPER +1];
  488.  
  489. /* Set up temp storage for RA and Dec data, etc.        */
  490.   static int flamint, ra_int, dec_int;
  491.   static int ra_hour, ra_min, ra_sec;
  492.   static char ns;
  493.   static int dec_deg, dec_min, dec_sec;
  494.   float fmag;
  495.  
  496. /* Read all non-optional items from the current line.   */
  497. /* Return FALSE if error (probably end of file).        */
  498.   if (fscanf(fileptr, fmt,
  499.         flamst_buf,
  500.         sptr->bayer,
  501.         constellbuf,
  502.         &sptr->catlg,
  503.         &ra_hour, &ra_min, &ra_sec,
  504.         &ns, &dec_deg, &dec_min, &dec_sec,
  505.         &fmag)
  506.         != 12) return FALSE;
  507.  
  508. /* Attempt to convert Flamsteed string into an integer. */
  509. /* Set Flamsteed no. to 0 if failure (probably blank).  */
  510.   if (sscanf(flamst_buf, "%d", &flamint) == 1)
  511.     sptr->flamst = (unsigned char)flamint;
  512.   else
  513.     sptr->flamst = 0;
  514.  
  515. /* Trim trailing blanks from Bayer string.              */
  516.   trim(sptr->bayer);
  517.  
  518. /* Convert constellation abbrev. into a number.         */
  519.   sptr->constell = string_search(constell_abb, constellbuf);
  520.  
  521. /* Calculate Right Ascension in radians.                */
  522.   ra_int   = 3600*ra_hour + 60*ra_min + ra_sec;
  523.   sptr->ra = CONV * (REAL)ra_int * (REAL)15.0 / (REAL)3600.0;
  524.  
  525. /* Calculate Declination in radians.                    */
  526.   dec_int   = 3600*dec_deg + 60*dec_min + dec_sec;
  527.   sptr->dec = CONV * (REAL)dec_int / (REAL)3600.0;
  528.   if (ns == 'S' || ns == 's') sptr->dec = -(sptr->dec);
  529.  
  530. /* Convert float fmag to REAL.                          */
  531.   sptr->mag = (REAL)fmag;
  532.  
  533. /* Derive magnitude group from magnitude value.         */
  534.   sptr->maggrp = maggrp(sptr->mag);
  535.  
  536. /* Try to read optional proper name into name_buf.      */
  537.   read_optional(name_buf, MAX_PROPER);
  538. /* If proper name is present, and space can be allocated*/
  539. /* to store it, fill in pointer field appropriately.    */
  540.   if (name_buf[0] != '\0' &&
  541.       (sptr->proper = malloc(strlen(name_buf)+1)) != NULL)
  542.     /* Copy name to storage area. Trim trailing blanks. */
  543.     strcpy(sptr->proper, trim(name_buf));
  544.   else
  545.     /* Set pointer field to point to null string.       */
  546.     sptr->proper = no_proper_name;
  547.  
  548.   return TRUE;
  549. }
  550.  
  551. /*------------------------------------------------------*/
  552. /*      Function which allocates a magnitude group.     */
  553. /*------------------------------------------------------*/
  554. static int maggrp(REAL mag)
  555. {
  556. /* Allocate a group number in the range                 */
  557. /* 0 <= grp <= NUMGRPS-1.  0 is dimmest (highest mag #s)*/
  558.  
  559.   int grp;
  560.  
  561.   for (grp = 0; grp < NUMGRPS-1; grp++)
  562.     if (mag >= grpbounds[grp]) break;
  563.  
  564.   return grp;
  565. }
  566.  
  567. /*------------------------------------------------------*/
  568. /*  Function which trims trailing blanks from a string. */
  569. /*------------------------------------------------------*/
  570. static char *trim(char *str)
  571. {
  572.   char *ptr;
  573.  
  574.   for (ptr = str+strlen(str); ptr>str && *(ptr-1)==' '; ptr--)
  575.     ;
  576.   *ptr = '\0';
  577.  
  578.   return str;
  579. }
  580.  
  581. /*------------------------------------------------------*/
  582. /* Function to read optional Proper Name at end of line.*/
  583. /*         Set string to null if name is absent.        */
  584. /*------------------------------------------------------*/
  585. static void read_optional(char *string, int count)
  586. {
  587.   int c;
  588.  
  589. /* Skip any leading white space (except '\n').          */
  590.   do
  591.     c = fgetc(fileptr);
  592.   while (isspace(c) && c != '\n');
  593.  
  594. /* Read characters into string until:                   */
  595. /*      'count' chars have been read in,                */
  596. /* or   \n is seen (in which case do not store \n),     */
  597. /* or   EOF is seen.                                    */
  598.   while (count>0  &&  c!='\n'  &&  c!=EOF)
  599.   {
  600.     *string++ = c;
  601.     count--;
  602.     c = fgetc(fileptr);
  603.   }
  604.  
  605. /* Add terminating \0.                                  */
  606.   *string = '\0';
  607.  
  608. /* If neither \n nor EOF was seen, discard remainder    */
  609. /* of current line.                                     */
  610.   while (c!='\n' && c!=EOF) c = fgetc(fileptr);
  611.  
  612.   return;
  613. }
  614.  
  615. /*------------------------------------------------------*/
  616. /*  Function to search a set of character strings for   */
  617. /*  a specified target.  The function returns the index */
  618. /* number of the matching string, or 0 if no match.     */
  619. /* (String zero is assumed to be some error message and */
  620. /*  not part of the data.)                              */
  621. /*------------------------------------------------------*/
  622. static int string_search(char *ptrs[], char *targ)
  623. {
  624.   int el = 1;
  625.  
  626.   for ( ; ptrs[el] != NULL; el++)
  627.     if (begins_with(ptrs[el], targ)) return el;
  628.  
  629.   return 0;
  630. }
  631.  
  632. /*------------------------------------------------------*/
  633. /* Function to test whether string a begins with string */
  634. /*             b (irrespective of case).                */
  635. /*------------------------------------------------------*/
  636. static BOOL begins_with(char *a, char *b)
  637. {
  638.   while (*a && *b)
  639.     if (tolower(*a++) != tolower(*b++)) return FALSE;
  640.  
  641.   return (!*b);
  642. }
  643.  
  644. /********************************************************/
  645. /*                 List-Building Function               */
  646. /********************************************************/
  647. static void star_buildfn(void)
  648. {
  649.   int i;
  650.   plotobj star;
  651.  
  652.   /* Bounding box of plotting symbol, relative to       */
  653.   /* position of symbol:                                */
  654.   static wimp_box size = {SPR_X0, SPR_Y0, SPR_X1, SPR_Y1};
  655.  
  656. /* If module is currently disabled, ignore call to list-*/
  657. /* building function, and set flag to indicate that     */
  658. /* module does not have valid data in the plotting list.*/
  659.   if (!display_flag)
  660.   {
  661.     list_valid = FALSE;
  662.     return;
  663.   }
  664.  
  665. /* If module is currently enabled, build plotting list  */
  666. /* and set flag to indicate module has valid data.      */
  667.   list_valid = TRUE;
  668.  
  669. /* For each star in turn:                             */
  670.   for (i=0; i<star_count; i++)
  671.   {
  672.  
  673.     /* Build plotobj.                                   */
  674.     star.id = i;
  675.     star.module = moduleid;
  676.     star.plotfn  = star_plotfn;
  677.     radec_altaz(star_data[i].ra, star_data[i].dec, &ob_data, ob_data.sid,
  678.                 &star.alt, &star.azim);
  679.     star.size = size;
  680.  
  681.     /* Offer this to the main program.                  */
  682.     addobj(star, &star_data[i].horiz_id, &star_data[i].vert_id);
  683.  
  684.   }
  685.  
  686.   return;
  687. }
  688.  
  689. /********************************************************/
  690. /*                   Display Function                   */
  691. /********************************************************/
  692. static BOOL star_displayfn(BOOL *enabptr)
  693. {
  694.   BOOL changes_made;  /* Have display options changed?  */
  695.  
  696. /* Respond to click on display menu entry, or to move-  */
  697. /* ment of pointer over arrow on menu entry.            */
  698.  
  699. /* Find out whether or not event was a menu choice.     */
  700. /* If it was, toggle the display on/off & return.       */
  701.   if (wimpt_last_event()->e == wimp_EMENU)
  702.   {
  703.     toggle_display(enabptr);
  704.     /* Windows will always need updating:               */
  705.     return TRUE;
  706.   }
  707.  
  708. /* Must be to do with the dialogue box.                 */
  709. /* Create dialogue box, load it with info and put it on */
  710. /* display (if not already done).                       */
  711.   if (!dbox_persisting)
  712.   {
  713.     d_disp = dbox_new(DISP_BOX);
  714.     if (d_disp == NULL) return FALSE;
  715.     disp_handle = dbox_syshandle(d_disp);
  716.     load_dispbox(which_sprite);
  717.     dbox_show(d_disp);
  718.   }
  719.  
  720. /* Deal with button clicks until action by main program */
  721. /* is required.  Fn returns FALSE if no changes made.   */
  722.   changes_made = dispbox_buttons();
  723.  
  724. /* Delete dbox if it is not required to persist.        */
  725.   if (!dbox_persisting) dbox_dispose(&d_disp);
  726.  
  727. /* Windows will need updating if changes were made and  */
  728. /* the module is currently enabled.                     */
  729.   return changes_made && display_flag;
  730. }
  731.  
  732. /*------------------------------------------------------*/
  733. /*    Function to toggle on/off the display of stars.   */
  734. /*------------------------------------------------------*/
  735. static void toggle_display(BOOL *enabptr)
  736. {
  737. /* Toggle Enable/Disable flag.                          */
  738.   display_flag = !display_flag;
  739.  
  740. /* Inform main program of new status.                   */
  741.   *enabptr = display_flag;
  742.  
  743. /* If module is now enabled, but does not have valid    */
  744. /* data in the plotting list, call the list-building fn.*/
  745.   if (display_flag && !list_valid)
  746.     star_buildfn();
  747.  
  748.   return;
  749. }
  750.  
  751. /*------------------------------------------------------*/
  752. /*  Function to load Display dbox with specified data.  */
  753. /*    table[] is a table of NUMGRPS sprite numbers.     */
  754. /*------------------------------------------------------*/
  755. static void load_dispbox(int table[])
  756. {
  757.   int i;
  758.  
  759.   for (i=0; i<NUMGRPS; i++)
  760.     wimpt_noerr(set_sprite(i, table[i]));
  761.  
  762.   return;
  763. }
  764.  
  765. /*------------------------------------------------------*/
  766. /*  Function to set the dbox sprite for group 'grpnum'  */
  767. /*             to sprite number 'sprnum'.               */
  768. /*------------------------------------------------------*/
  769. static os_error *set_sprite(int grpnum, int sprnum)
  770. {
  771.   wimp_icreate ic;
  772.   os_error *errptr;
  773.  
  774. /* Get existing data on the icon used for group grpnum. */
  775.   errptr = wimp_get_icon_info(disp_handle, iconhandles[grpnum], &ic.i);
  776.   if (errptr != NULL) return errptr;
  777.  
  778. /* Delete old icon.                                     */
  779.   errptr = wimp_delete_icon(disp_handle, iconhandles[grpnum]);
  780.   if (errptr != NULL) return errptr;
  781.  
  782. /* Change sprite name in icon block.                    */
  783. /* Copy at most 12 chars from disp_names[sprnum].       */
  784.   strncpy(ic.i.data.sprite_name, disp_names[sprnum], 12);
  785.  
  786. /* Create new icon.                                     */
  787.   ic.w = disp_handle;
  788.   errptr = wimp_create_icon(&ic, &iconhandles[grpnum]);
  789.   if (errptr != NULL) return errptr;
  790.  
  791. /* Make a note of which sprite is now being used for    */
  792. /* magnitude group grpnum in the Display dbox.          */
  793.   disp_sprites[grpnum] = sprnum;
  794.  
  795. /* Force re-draw of affected part of dbox.              */
  796.   errptr = wimp_force_redraw((wimp_redrawstr *)&ic);
  797.  
  798.   return errptr;
  799. }
  800.  
  801. /*------------------------------------------------------*/
  802. /*  Function to handle clicks on buttons in Disp dbox.  */
  803. /*  Returns TRUE if display options have been changed.  */
  804. /*------------------------------------------------------*/
  805. static BOOL dispbox_buttons(void)
  806. {
  807.   int response;  /* No. of button clicked by user.      */
  808.   BOOL changes_made = FALSE;  /* Any options changed?   */
  809.   int grpnum;    /*Mag group whose sprite needs changing*/
  810.   int sprnum;    /*Sprite number to use in that group.  */
  811.  
  812. /* Loop until user clicks on OK, or on Cancel with left */
  813. /* hand mouse button, or destroys dbox with a 'harmful  */
  814. /* event' (eg mouse click outside box).                 */
  815.  
  816.   while (TRUE)
  817.   {
  818.  
  819.   /* Get button number of latest click.                 */
  820.     response = dbox_fillin(d_disp);
  821.  
  822.   /* Take appropriate action.                           */
  823.     switch (response) {
  824.  
  825.       case disp_destroy:
  826.       /* Dbox destroyed.  Close it & return.            */
  827.         dbox_persisting = FALSE;
  828.         return FALSE;
  829.  
  830.       case disp_ok:
  831.       /* Return with the sprite table set to the new    */
  832.       /* values, indicating whether these differ from   */
  833.       /* the old ones.  Retain dbox if click was with   */
  834.       /* Adjust.  Can safely ignore clicks with Adjust  */
  835.       /* when there are no changes.                     */
  836.         changes_made = check_and_copy(which_sprite, disp_sprites);
  837.         dbox_persisting = dbox_persist();
  838.         if (dbox_persisting && !changes_made) break;
  839.         return changes_made;
  840.  
  841.       case disp_cancel:
  842.       /* Action depends on whether click was with Select*/
  843.       /* or Adjust.  If Select, just close dbox and     */
  844.       /* return.  If Adjust, copy original sprite table */
  845.       /* back into dbox and continue looping.           */
  846.         if (dbox_persist())
  847.         {
  848.           load_dispbox(which_sprite);
  849.           break;
  850.         }
  851.         else
  852.         {
  853.           dbox_persisting = FALSE;
  854.           return FALSE;
  855.         }
  856.  
  857.       case disp_defshow:
  858.       /* Read default table from disc, and load it into */
  859.       /* dbox if no errors detected.                    */
  860.         {
  861.           int temp_table[NUMGRPS];
  862.           if (table_fromdisc(temp_table))
  863.             load_dispbox(temp_table);
  864.           else
  865.             werr(NON_FATAL, "Can't read defaults");
  866.         }
  867.         break;
  868.  
  869.       case disp_defset:
  870.       /*Write current dbox settings to file of defaults.*/
  871.       /* If error, report it and continue.              */
  872.         if (!table_todisc())
  873.         {
  874.           werr(NON_FATAL, "Error setting defaults");
  875.           break;
  876.         }
  877.       /* If click was with Select, close dbox and       */
  878.       /* return.  No need to update the windows.        */
  879.         if (!dbox_persist())
  880.         {
  881.           dbox_persisting = FALSE;
  882.           return FALSE;
  883.         }
  884.         break;
  885.  
  886.       case disp_dim0: case disp_dim1: case disp_dim2: case disp_dim3:
  887.       case disp_dim4: case disp_dim5: case disp_dim6: case disp_dim7:
  888.       /* Make a sprite dimmer.                          */
  889.         grpnum = (response - disp_dim0)/4;
  890.         sprnum = (disp_sprites[grpnum] + NUMSPRITES - 1)%NUMSPRITES;
  891.         wimpt_noerr(set_sprite(grpnum, sprnum));
  892.         break;
  893.  
  894.       case disp_brt0: case disp_brt1: case disp_brt2: case disp_brt3:
  895.       case disp_brt4: case disp_brt5: case disp_brt6: case disp_brt7:
  896.       /* Make a sprite brighter.                        */
  897.         grpnum = (response - disp_dim0)/4;
  898.         sprnum = (disp_sprites[grpnum] + 1)%NUMSPRITES;
  899.         wimpt_noerr(set_sprite(grpnum, sprnum));
  900.         break;
  901.  
  902.       default:
  903.       /* Unknown response.  Do nothing.                 */
  904.         break;
  905.  
  906.     }
  907.   }
  908. }
  909.  
  910. /*------------------------------------------------------*/
  911. /*  Function to write the current dbox sprite table to  */
  912. /*    the disc file containing the default settings.    */
  913. /*  Return TRUE if no errors detected, FALSE otherwise. */
  914. /*------------------------------------------------------*/
  915. static BOOL table_todisc(void)
  916. {
  917.   FILE *defaults;
  918.   size_t nobj;
  919.  
  920. /* Attempt to open file for writing.                    */
  921.   defaults = res_openfile(DEF_FILE, "wb");
  922. /* Give up if file will not open.                       */
  923.   if (defaults == NULL) return FALSE;
  924.  
  925. /* Attempt to write out dbox sprite table.              */
  926.   nobj = fwrite(disp_sprites, sizeof(int), NUMGRPS, defaults);
  927.  
  928. /* Close file.  Fatal error if it won't close.          */
  929.   if (fclose(defaults) != 0)
  930.     werr(FATAL, "Stars Error: Can't close Default file");
  931.  
  932. /* Return success or failure.                           */
  933.   return (nobj == NUMGRPS);
  934. }
  935.  
  936. /*------------------------------------------------------*/
  937. /*  Function to read default sprite settings from disc  */
  938. /*  into the sprite table spr_table. Return TRUE if no  */
  939. /*  errors detected, else return FALSE with spr_table   */
  940. /*                      undefined.                      */
  941. /*------------------------------------------------------*/
  942. static BOOL table_fromdisc(int spr_table[])
  943. {
  944.   FILE *defaults;
  945.   size_t nobj;
  946.  
  947. /* Attempt to open file of defaults.                    */
  948.   defaults = res_openfile(DEF_FILE, "rb");
  949. /* Give up if file will not open.                       */
  950.   if (defaults == NULL) return FALSE;
  951.  
  952. /* Attempt to read sprite table.                        */
  953.   nobj = fread(spr_table, sizeof(int), NUMGRPS, defaults);
  954.  
  955. /* Close file.  Fatal error if it won't close.          */
  956.   if (fclose(defaults) != 0)
  957.     werr(FATAL, "Stars Error: Can't close Default file");
  958.  
  959. /* Return success or failure.                           */
  960.   return (nobj == NUMGRPS);
  961. }
  962.  
  963. /*------------------------------------------------------*/
  964. /* Function to copy sprite table 2 into sprite table 1. */
  965. /*          Return TRUE if the two differed.            */
  966. /*------------------------------------------------------*/
  967. static BOOL check_and_copy(int spr_tab1[], int spr_tab2[])
  968. {
  969.   int i;
  970.   BOOL changed = FALSE;
  971.  
  972.   for (i=0; i<NUMGRPS; i++)
  973.     if (spr_tab1[i] != spr_tab2[i])
  974.     {
  975.       changed = TRUE;
  976.       spr_tab1[i] = spr_tab2[i];
  977.     }
  978.  
  979.   return changed;
  980. }
  981.  
  982.  
  983.   
  984. /********************************************************/
  985. /*              Object-Selecting Function               */
  986. /********************************************************/
  987. static void star_selectfn(selectfn_reasoncode reason)
  988. {
  989.   int star_id;
  990.  
  991.   switch (reason)
  992.   {
  993.     case new_selection:
  994.       /* By dialogue with the user, get the id number of*/
  995.       /* the star to be selected.  Use the selection    */
  996.       /* method specified by the sub-menu choice.  Do   */
  997.       /* nothing if the requested method is unknown.    */
  998.  
  999.       /* Set 'success' flag to FALSE initially.         */
  1000.       selection.selected_OK = FALSE;
  1001.       method = module_submenu_hits[0];
  1002.       if (method <= 0)          return;
  1003.       if (method > SEL_METHODS) return;
  1004.       star_id = selectionboxes();
  1005.       /* ID of -1 indicates dialogue was cancelled.     */
  1006.       if (star_id == -1) return;
  1007.  
  1008.       /* Valid selection has been made.                 */
  1009.       selection.id = star_id;
  1010.       /*  Flag the selection as successful.             */
  1011.       selection.selected_OK = TRUE;
  1012.       /* Fill in Selection details for requested star.  */
  1013.       selection_details();
  1014.       break;
  1015.  
  1016.     case window_selection:
  1017.       /* A selection has been made by clicking in a     */
  1018.       /* window.                                        */
  1019.     case recalculate_data:
  1020.       /* Observer details have changed, and plotting    */
  1021.       /* lists have been rebuilt.                       */
  1022.  
  1023.       /* In both cases, the ID of the star is already   */
  1024.       /* in selection.id                                */
  1025.       selection_details();
  1026.       break;
  1027.  
  1028.     case timeonly_recalculate:
  1029.       /* As recalculate_data, but only the time of day  */
  1030.       /* (and possibly the direction of view - this is  */
  1031.       /* is of no interest) have changed.               */
  1032.       /* This simplifies the calculation.               */
  1033.       star_id = selection.id;
  1034.       selection.horiz_id    = star_data[star_id].horiz_id;
  1035.       selection.vert_id     = star_data[star_id].vert_id;
  1036.       /* Decide if it can be seen now.                  */
  1037.       selection.now         = selection.horiz_id != NOWHERE || \
  1038.                               selection.vert_id  != NOWHERE;
  1039.       break;
  1040.  
  1041.     case sel_info_request:
  1042.       /* Write info into text buffer.                   */
  1043.       /* Use the same fn as for clicks in window.       */
  1044.       star_infofn(selection.id);
  1045.       break;
  1046.  
  1047.     default:
  1048.       /* Unknown option.  Do nothing.                   */
  1049.       break;
  1050.  
  1051.     }
  1052.  
  1053.   return;
  1054. }
  1055.  
  1056. /*------------------------------------------------------*/
  1057. /* Function to obtain id number of marker to be selected*/
  1058. /* by means of a dialogue box.  Global 'method' specif- */
  1059. /* ies which selection method to use.                   */
  1060. /*         Returns -1 if dialogue is cancelled.         */
  1061. /*------------------------------------------------------*/
  1062. static int selectionboxes()
  1063. {
  1064.   int result;              /* Result of dialogue.       */
  1065.  
  1066. /* Ensure menu has gone, create dbox, load it with info,*/
  1067. /* and put it on display.  (If not already done.)       */
  1068.   if (!dbox_persisting)
  1069.   {
  1070.     event_clear_current_menu();
  1071.     d_sel = dbox_new(selbox_names[method-1]);
  1072.     if (d_sel == NULL) return -1;
  1073.     selbox_load();
  1074.     dbox_show(d_sel);
  1075.   }
  1076.  
  1077. /* Deal with button clicks until user clicks OK, or     */
  1078. /* clicks Cancel with left hand mouse button, or clicks */
  1079. /* outside dbox. Result is chosen id, or -1 if cancelled*/
  1080.   result = selbox_buttons();
  1081.  
  1082. /* Delete dbox if it is not required to persist.        */
  1083.   if (!dbox_persisting) dbox_dispose(&d_sel);
  1084.  
  1085. /* Return the chosen value.                             */
  1086.   return result;
  1087. }
  1088.  
  1089. /*------------------------------------------------------*/
  1090. /*  Function to load the previous strings back into the */
  1091. /* writeable icons in the appropriate Selection dbox.   */
  1092. /*   Also set current search criteria to correspond.    */
  1093. /*------------------------------------------------------*/
  1094. static void selbox_load(void)
  1095. {
  1096.   switch (method)
  1097.   {
  1098.     case by_proper:
  1099.       dbox_setfield(d_sel, sel_writeable1, prev_proper);
  1100.       strcpy(search1, prev_proper);
  1101.       break;
  1102.  
  1103.     case by_bayer:
  1104.       dbox_setfield(d_sel, sel_writeable1, prev_bayer);
  1105.       dbox_setfield(d_sel, sel_writeable2, prev_constell_b);
  1106.       strcpy(search1, prev_bayer);
  1107.       strcpy(search2, prev_constell_b);
  1108.       break;
  1109.  
  1110.     case by_flamst:
  1111.       if (prev_flamst > 0)
  1112.         dbox_setnumeric(d_sel, sel_writeable1, prev_flamst);
  1113.       else
  1114.         dbox_setfield(d_sel, sel_writeable1, "");
  1115.       dbox_setfield(d_sel, sel_writeable2, prev_constell_f);
  1116.       search_int = prev_flamst;
  1117.       strcpy(search2, prev_constell_f);
  1118.       break;
  1119.  
  1120.     case by_catlg:
  1121.       if (prev_catlg >= 0)
  1122.         dbox_setnumeric(d_sel, sel_writeable1, prev_catlg);
  1123.       else
  1124.         dbox_setfield(d_sel, sel_writeable1, "");
  1125.       search_int = prev_catlg;
  1126.       break;
  1127.  
  1128.     default:
  1129.       break;
  1130.   }
  1131.  
  1132. /*Clear the fields displaying the currently offered star*/
  1133.   dbox_setfield(d_sel, sel_star_id, "");
  1134.   dbox_setfield(d_sel, sel_constell_id, "");
  1135.   dbox_setfield(d_sel, sel_proper, "");
  1136.  
  1137. /* Fade the OK button.                                  */
  1138.   dbox_fadefield(d_sel, sel_ok);
  1139.   dbox_raw_eventhandler(d_sel, no_return_key, NULL);
  1140.  
  1141.   return;
  1142. }
  1143.  
  1144. /*------------------------------------------------------*/
  1145. /* Function to deal with button clicks in Selection box.*/
  1146. /*------------------------------------------------------*/
  1147. static int selbox_buttons(void)
  1148. {
  1149.   int response;    /* Latest button No. clicked by user.*/
  1150.  
  1151.   static int offered_id; /*ID of star currently on offer*/
  1152.  
  1153. /* If this is a repeated call, leave offered_id set to  */
  1154. /* its previous value.  Otherwise reset it so that      */
  1155. /* searching begins at the start of the database.       */
  1156.   if (!dbox_persisting) offered_id = star_count;
  1157.  
  1158. /* Loop until user clicks on OK, or on Cancel with left-*/
  1159. /* hand mouse button, or closes dbox with a 'harmful    */
  1160. /* event' (eg clicking outside dbox).                   */
  1161.   while (TRUE)
  1162.   {
  1163.  
  1164.   /* Get field number of latest click.                  */
  1165.     response = dbox_fillin(d_sel);
  1166.  
  1167.   /* Take appropriate action.                           */
  1168.     switch (response) {
  1169.  
  1170.       case sel_destroy:
  1171.       /* dbox cancelled.  Close it and return -1.       */
  1172.         dbox_persisting = FALSE;
  1173.         return -1;
  1174.  
  1175.       case sel_ok:
  1176.       /* Save current search criteria as 'previous      */
  1177.       /* values' of the appropriate writeable icons.    */
  1178.         save_searchdata();
  1179.       /* Retain dbox if click was with adjust.          */
  1180.         dbox_persisting = dbox_persist();
  1181.       /* Return id of star currently on offer.          */
  1182.         return offered_id;
  1183.  
  1184.       case sel_cancel:
  1185.       /* If normal click, close dbox and return -1.     */
  1186.       /* If Adjust, reload saved search data & continue.*/
  1187.         if (dbox_persist())
  1188.         {
  1189.           selbox_load();
  1190.           offered_id = star_count;
  1191.         }
  1192.         else
  1193.         {
  1194.           dbox_persisting = FALSE;
  1195.           return -1;
  1196.         }
  1197.         break;
  1198.  
  1199.       case sel_search:
  1200.       /* Reset offered_id so that search begins at start*/
  1201.         offered_id = star_count;
  1202.       /* Run on into case sel_continue - NO BREAK.      */
  1203.  
  1204.       case sel_continue:
  1205.       /* Search from current value of offered_id.       */
  1206.       /* Read search criteria from writeable icons.     */
  1207.         read_searchdata();
  1208.       /* Find next star meeting those criteria. If none,*/
  1209.       /* offered_id gets set equal to star_count.       */
  1210.         find_next_star(&offered_id);
  1211.         if (offered_id < star_count)
  1212.         {
  1213.         /* Success. Display full id of star. Enable 'OK'*/
  1214.           display_id(offered_id);
  1215.           dbox_unfadefield(d_sel, sel_ok);
  1216.           dbox_raw_eventhandler(d_sel, NULL, NULL);
  1217.         }
  1218.         else
  1219.         {
  1220.         /* Failure. Display message. Disable 'OK'.      */
  1221.           dbox_setfield(d_sel, sel_star_id, "");
  1222.           dbox_setfield(d_sel, sel_constell_id, "<Not found>");
  1223.           dbox_setfield(d_sel, sel_proper, "");
  1224.           dbox_fadefield(d_sel, sel_ok);
  1225.           dbox_raw_eventhandler(d_sel, no_return_key, NULL);
  1226.         }
  1227.         break;
  1228.     }
  1229.   }
  1230. }
  1231.  
  1232. /*------------------------------------------------------*/
  1233. /*    Raw event handler for Select dbox, to prevent     */
  1234. /* 'return' key from triggering Cancel button when 'OK' */
  1235. /*                     is disabled.                     */
  1236. /*------------------------------------------------------*/
  1237. static BOOL no_return_key(dbox d, void *event, void *handle)
  1238. {
  1239.   wimp_eventstr *e = (wimp_eventstr *)event;
  1240.  
  1241.   if (e->e == wimp_EKEY && e->data.key.chcode == '\r')
  1242.     return TRUE;
  1243.  
  1244.   return FALSE;
  1245. }
  1246.  
  1247. /*------------------------------------------------------*/
  1248. /*      Function to save current search criteria.       */
  1249. /*------------------------------------------------------*/
  1250. static void save_searchdata(void)
  1251. {
  1252.   switch (method) {
  1253.  
  1254.     case by_proper:
  1255.       strcpy(prev_proper, search1);
  1256.       break;
  1257.  
  1258.     case by_bayer:
  1259.       strcpy(prev_bayer, search1);
  1260.       strcpy(prev_constell_b, search2);
  1261.       break;
  1262.  
  1263.     case by_flamst:
  1264.       prev_flamst = search_int;
  1265.       strcpy(prev_constell_f, search2);
  1266.       break;
  1267.  
  1268.     case by_catlg:
  1269.       prev_catlg = search_int;
  1270.       break;
  1271.   }
  1272.   return;
  1273. }
  1274.  
  1275. /*------------------------------------------------------*/
  1276. /* Function to read new search criteria from writeable  */
  1277. /*      icons.  Return TRUE if they have changed.       */
  1278. /*------------------------------------------------------*/
  1279. static BOOL read_searchdata(void)
  1280. {
  1281.   BOOL changed = FALSE;
  1282.  
  1283. /* Temporary storage for newly-read values:             */
  1284.   char s1buff[MAX_FIELD+1];
  1285.   char s2buff[MAX_FIELD+1];
  1286.   int  s;
  1287.  
  1288.   switch (method) {
  1289.  
  1290.     case by_proper:
  1291.     /* Read string from dbox.                           */
  1292.       dbox_getfield(d_sel, sel_writeable1, s1buff, MAX_FIELD+1);
  1293.     /* See if it differs significantly from current one.*/
  1294.       changed = !is_same(search1, s1buff);
  1295.     /* Make new string the current one.                 */
  1296.       strcpy(search1, s1buff);
  1297.       break;
  1298.  
  1299.     case by_bayer:
  1300.     /* Read strings from dbox.                          */
  1301.       dbox_getfield(d_sel, sel_writeable1, s1buff, MAX_FIELD+1);
  1302.       dbox_getfield(d_sel, sel_writeable2, s2buff, MAX_FIELD+1);
  1303.     /* Any significant changes?                         */
  1304.       changed = !is_same(search1, s1buff) || !is_same(search2, s2buff);
  1305.     /* Make them current.                               */
  1306.       strcpy(search1, s1buff);
  1307.       strcpy(search2, s2buff);
  1308.       break;
  1309.  
  1310.     case by_flamst:
  1311.     /* Read data from dbox.                             */
  1312.       s = dbox_getnumeric(d_sel, sel_writeable1);
  1313.       dbox_getfield(d_sel, sel_writeable2, s2buff, MAX_FIELD+1);
  1314.     /* Any significant changes?                         */
  1315.       changed = (search_int != s) || !is_same(search2, s2buff);
  1316.     /* Make new data the current ones.                  */
  1317.       search_int = s;
  1318.       strcpy(search2, s2buff);
  1319.       break;
  1320.  
  1321.     case by_catlg:
  1322.     /* Read catalogue number from dbox.                 */
  1323.       s = dbox_getnumeric(d_sel, sel_writeable1);
  1324.     /* Write it back to show what the number actually is*/
  1325.       dbox_setnumeric(d_sel, sel_writeable1, s);
  1326.     /* Make newly read value current if diff. from prev.*/
  1327.       changed = (search_int != s);
  1328.       if (changed) search_int = s;
  1329.       break;
  1330.  
  1331.   }
  1332.   return changed;
  1333. }
  1334.  
  1335. /*------------------------------------------------------*/
  1336. /*   Function to test whether string a is the same as   */
  1337. /*             string b (disregarding case).            */
  1338. /*------------------------------------------------------*/
  1339. static BOOL is_same(char *a, char *b)
  1340. {
  1341.   while (*a && *b)
  1342.     if (tolower(*a++) != tolower(*b++)) return FALSE;
  1343.  
  1344.   return (!*a && !*b);
  1345. }
  1346.  
  1347. /*------------------------------------------------------*/
  1348. /* Function to find the next star in the database which */
  1349. /*              meets the given criteria.               */
  1350. /* If no star found, return with *id_ptr = star_count.  */
  1351. /*------------------------------------------------------*/
  1352. static void find_next_star(int *id_ptr)
  1353. {
  1354.   BOOL star_found = FALSE; /* 'Success' flag.           */
  1355.   int  id;                 /* Data                      */
  1356.   char *proper;            /* from                      */
  1357.   char *bayer;             /* the                       */
  1358.   int  constell;           /* star                      */
  1359.   int  flamst;             /* currently                 */
  1360.   int  catlg;              /* being checked.            */
  1361.  
  1362. /* If the current star ID is greater than the highest   */
  1363. /* valid ID number, reset it to zero.  Otherwise        */
  1364. /* increment it to indicate the next star.              */
  1365.   if (*id_ptr >= star_count)
  1366.     *id_ptr = 0;
  1367.   else
  1368.     (*id_ptr)++;
  1369.  
  1370. /* Go through the database from the specified position, */
  1371. /* looking for a star matching the current criteria.    */
  1372.   while (*id_ptr < star_count)
  1373.   {
  1374.     id = *id_ptr;
  1375.  
  1376.     switch (method) {
  1377.  
  1378.       case by_proper:
  1379.         proper = star_data[id].proper;
  1380.         star_found = begins_with_inlist(proper, search1);
  1381.         if (star_found) return;
  1382.         break;
  1383.  
  1384.       case by_bayer:
  1385.         bayer    = star_data[id].bayer;
  1386.         constell = star_data[id].constell;
  1387.         star_found = begins_with(bayer, search1) &&
  1388.                      (begins_with(constell_abb[constell], search2) ||
  1389.                       begins_with(constell_gen[constell], search2) ||
  1390.                       begins_with(constell_nom[constell], search2) );
  1391.         if (star_found) return;
  1392.         break;
  1393.  
  1394.       case by_flamst:
  1395.         flamst   = star_data[id].flamst;
  1396.         constell = star_data[id].constell;
  1397.         /* Let a search_int of 0 match anything.        */
  1398.         star_found = (flamst == search_int || search_int == 0) &&
  1399.                      (begins_with(constell_abb[constell], search2) ||
  1400.                       begins_with(constell_gen[constell], search2) ||
  1401.                       begins_with(constell_nom[constell], search2) );
  1402.         if (star_found) return;
  1403.         break;
  1404.  
  1405.       case by_catlg:
  1406.         catlg = star_data[id].catlg;
  1407.         star_found = (catlg == search_int);
  1408.         if (star_found) return;
  1409.         break;
  1410.  
  1411.     }
  1412.  
  1413.   /* Try next star.                                     */
  1414.     (*id_ptr)++;
  1415.   }
  1416.  
  1417.   return;
  1418. }
  1419.  
  1420. /*------------------------------------------------------*/
  1421. /*   Function to test whether any of the items listed   */
  1422. /*      in the string 'a' begin with the string 'b'.    */
  1423. /*------------------------------------------------------*/
  1424. static BOOL begins_with_inlist(char *a, char *b)
  1425. {
  1426. /* Check first item (possibly only item).               */
  1427.   if (begins_with(a,b)) return TRUE;
  1428.  
  1429. /* Other items are assumed to be separated by ", ".     */
  1430.   while ( (a=strstr(a,", ")) != NULL )
  1431.   {
  1432.     a += 2;
  1433.     if (begins_with(a, b)) return TRUE;
  1434.   }
  1435.  
  1436.   return FALSE;
  1437. }
  1438.  
  1439. /*------------------------------------------------------*/
  1440. /*     Function to display full ID of offered star.     */
  1441. /*------------------------------------------------------*/
  1442. static void display_id(int id)
  1443. {
  1444.   char id_buff[MAX_FIELD+1];
  1445.  
  1446. /* Get ID(s) of star into text buffer.                  */
  1447.   id_string(id, id_buff);
  1448.  
  1449. /* Write ID data into dialogue box.                     */
  1450.   dbox_setfield(d_sel, sel_star_id, id_buff);
  1451.   dbox_setfield(d_sel, sel_constell_id,
  1452.                 constell_gen[(int)star_data[id].constell]);
  1453.   dbox_setfield(d_sel, sel_proper, star_data[id].proper);
  1454.  
  1455.   return;
  1456. }
  1457.  
  1458.  
  1459. /*------------------------------------------------------*/
  1460. /*  Function to fill in selection details of selected   */
  1461. /*    star (viewing directions and times for rising,    */
  1462. /*             setting, culminating etc).               */
  1463. /*------------------------------------------------------*/
  1464. static void selection_details(void)
  1465. {
  1466.   double ch;     /* cos of hour angle at horizon.       */
  1467.   BOOL riset;    /* Whether object rises &sets or not.  */
  1468.   BOOL culmin;   /* Whether object culminates or not.   */
  1469.  
  1470. /* ID number of star is in selection.id                 */
  1471.   int id = selection.id;
  1472.  
  1473. /* Record window IDs of selected star.                  */
  1474.   selection.horiz_id    = star_data[id].horiz_id;
  1475.   selection.vert_id     = star_data[id].vert_id;
  1476.  
  1477. /* Decide if it can be seen now.                        */
  1478.   selection.now         = selection.horiz_id != NOWHERE || \
  1479.                           selection.vert_id  != NOWHERE;
  1480.  
  1481. /* Find out if star rises, sets or culminates today.    */
  1482. /* (After setting effective altitude of horizon.)       */
  1483.   radec_sethoriz(HORALT);
  1484.  
  1485.   radec_phenomena(star_data[id].ra, star_data[id].dec, &ob_data, 
  1486.                   &ch, &riset, &culmin);
  1487.   selection.rising      = riset;
  1488.   selection.setting     = riset;
  1489.   selection.culminating = culmin; 
  1490.  
  1491. /* Fill in details for each phenomenon which occurs.    */
  1492.  
  1493.   if (selection.rising)
  1494.     radec_rise_details(star_data[id].ra, star_data[id].dec, &ob_data, ch,
  1495.                        &selection.rise_azim,
  1496.                        &selection.rise_hour,
  1497.                        &selection.rise_min);
  1498.  
  1499.   if (selection.setting)
  1500.     radec_set_details(star_data[id].ra, star_data[id].dec, &ob_data, ch,
  1501.                       &selection.set_azim,
  1502.                       &selection.set_hour,
  1503.                       &selection.set_min);
  1504.  
  1505.   if (selection.culminating)
  1506.     radec_cul_details(star_data[id].ra, star_data[id].dec, &ob_data,
  1507.                       &selection.cul_alt,
  1508.                       &selection.cul_azim,
  1509.                       &selection.cul_hour,
  1510.                       &selection.cul_min);
  1511.  
  1512.   return;
  1513. }
  1514.  
  1515. /********************************************************/
  1516. /*                   Plotting Function                  */
  1517. /********************************************************/
  1518. static os_error *star_plotfn(int x, int y, int id)
  1519. {
  1520.   return sv_plotsprite(spr_id + which_sprite[star_data[id].maggrp],
  1521.                        SPR_MODE, x, y, SPR_COL, SPR_ROW);
  1522. }
  1523.  
  1524.  
  1525. /********************************************************/
  1526. /*                     Info Function                    */
  1527. /********************************************************/
  1528. static void star_infofn(int id)
  1529. {
  1530.   char id_buff[MAX_FIELD+1]; /*Text buffer for star ID.*/
  1531.   char p_text[RADEC_TEXTLEN];/*Text buffer for RA & Dec*/
  1532.  
  1533. /* Get star ID(s) into text buffer.                    */
  1534.   id_string(id, id_buff);
  1535.  
  1536. /* Get RA and Dec in text form.                        */
  1537.   radec_text(star_data[id].ra, star_data[id].dec, p_text);
  1538.  
  1539.   sprintf(infoptr,
  1540.     "%s%.1f\n%s\n%s\n%s\n%s",
  1541.     "Star, magnitude ",(double)star_data[id].mag,
  1542.     id_buff,
  1543.     constell_gen[(int)star_data[id].constell],
  1544.     star_data[id].proper,
  1545.     p_text);
  1546.  
  1547.   return;
  1548. }
  1549.  
  1550. /*------------------------------------------------------*/
  1551. /* Function to build a string with the Bayer, Flamsteed */
  1552. /*        and catalogue IDs of the given star.          */
  1553. /*   String pointed to by str is assumed long enough.   */
  1554. /*------------------------------------------------------*/
  1555. static void id_string(int id, char *str)
  1556. {
  1557.   int flamst  = star_data[id].flamst;
  1558.   char *bayer = star_data[id].bayer;
  1559.   int catlg   = star_data[id].catlg;
  1560.  
  1561. /* Handle case when Flamsteed number is present.        */
  1562.   if (flamst != 0)
  1563.   {
  1564.     sprintf(str, "%i  %s  (%i)", flamst, bayer, catlg);
  1565.     return;
  1566.   }
  1567.  
  1568. /* Handle case of Bayer but no Flamsteed.               */
  1569.   if (*bayer != '\0')
  1570.   {
  1571.     sprintf(str, "%s  (%i)", bayer, catlg);
  1572.     return;
  1573.   }
  1574.  
  1575. /* Handle case of no Bayer or Flamsteed.                */
  1576.   sprintf(str, "BS Catlg # %i", catlg);
  1577.  
  1578.   return;
  1579. }
  1580.